package site.hanzhe.security;

import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.security.config.annotation.authentication.builders.AuthenticationManagerBuilder;
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
import org.springframework.security.config.annotation.web.configuration.WebSecurityConfigurerAdapter;
import org.springframework.security.core.GrantedAuthority;
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
import site.hanzhe.entity.AdminEntity;
import site.hanzhe.entity.AuthEntity;
import site.hanzhe.entity.RoleEntity;
import site.hanzhe.service.AdminService;
import site.hanzhe.service.AuthService;
import site.hanzhe.service.RoleService;

import java.util.ArrayList;
import java.util.List;

/**
 * SpringSecurity配置类
 * EnableWebSecurity：开启基于WEB的授权验证
 * EnableGlobalMethodSecurity(prePostEnabled = true)：配置让一些注解生效
 */
@EnableWebSecurity
@EnableGlobalMethodSecurity(prePostEnabled = true)
public class SecurityConfig extends WebSecurityConfigurerAdapter {

    @Autowired
    private BCryptPasswordEncoder encoder;
    @Autowired
    private AdminService adminService;
    @Autowired
    private RoleService roleService;
    @Autowired
    private AuthService authService;

    // 设置请求授权相关设置
    public void setRequestAuth(HttpSecurity security) throws Exception {
        /*
        // 通过URL进行权限管理
        security.authorizeRequests()
                // 设置忽略权限验证的URL
                .antMatchers("/admin/to/login/page.html", "/admin/do/login.html", "/admin/do/logout.html", "/static/**")
                .permitAll()
                // 设置这些URL需要经理的角色才能访问
                .antMatchers("/admin/get/page.html")
                .hasRole("经理")
                // 剩下所有的页面只需要登录即可
                .anyRequest()
                .authenticated();
         */
        // 设置除开登录退出之外所有请求仅有登录验证，具体的权限验证会在Controller上用注解实现
        security.authorizeRequests()
                .antMatchers("/admin/to/login/page.html", "/admin/do/login.html", "/admin/do/logout.html", "/static/**")
                .permitAll()
                .anyRequest()
                .authenticated();
    }

    // 设置登录、退出的表单相关设置
    public void setForm(HttpSecurity security) throws Exception {
        security.formLogin()
                // 登录表单相关设置
                .loginPage("/admin/to/login/page.html")
                .usernameParameter("account")
                .passwordParameter("password")
                .loginProcessingUrl("/admin/do/login.html")
                .defaultSuccessUrl("/admin/to/main/page.html")
                // 退出表单相关配置
                .and().logout()
                .logoutUrl("/admin/do/logout.html")
                .logoutSuccessUrl("/admin/to/main/page.html");
    }

    @Override
    // 整合设置(表单、请求授权)
    protected void configure(HttpSecurity security) throws Exception {
        // 关闭跨站请求伪造拦截功能，方便后续的开发
        security.csrf().disable();
        this.setRequestAuth(security);
        this.setForm(security);
    }

    @Override
    // 设置数据库登录设置
    protected void configure(AuthenticationManagerBuilder builder) throws Exception {
        builder.userDetailsService((String account) -> {
            // 查询到管理员的信息，且查询管理员拥有的所有角色以及权限
            AdminEntity entity = adminService.selectAdminByAccount(account);
            List<RoleEntity> roleList = roleService.selectAssignRoleByAdminId(entity.getId());
            List<AuthEntity> authList = authService.selectAssignAuthByAdminId(entity.getId());
            List<GrantedAuthority> authorities = new ArrayList<>();
            // 将三个参数封装为一个UserDetails对象返回
            roleList.forEach(item -> authorities.add(new SimpleGrantedAuthority("ROLE_" + item.getName())));
            authList.forEach(item -> {
                if (item.getCategoryId() != null) {
                    authorities.add(new SimpleGrantedAuthority(item.getName()));
                }
            });
            return new AdminDetails(entity, authorities);
        }).passwordEncoder(encoder);
    }
}